


#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "rp2040.h"
#include "hardware/structs/usb.h"
#include "hardware/resets.h"

#include "ttusb_comn.h"
#include "ttusb.h"


#define usb_hw_set hw_set_alias(usb_hw)
#define usb_hw_clear hw_clear_alias(usb_hw)




//static usbd_config_t ucfg;
//static usbd_intf_t  uintfs[2];

static usbd_context_t uctx;

/**/
int ttusb_desc_init( void )
{
    usbd_context_t * pctx = &uctx;

    /**/
    memset( pctx, 0, sizeof(usbd_context_t) );
    return 0;
}

/* config: device */
int ttusb_desc_device_vid_pid( uint16_t vid, uint16_t pid )
{
    usbd_config_t * pcfg = &( uctx.ucfg );

    /**/
    pcfg->vid = vid;
    pcfg->pid = pid;
    return 0;
}


int ttusb_desc_device_str_manu( char * str )
{
    usbd_config_t * pcfg = &( uctx.ucfg );

    /**/
    pcfg->str_manu = str;
    return 0;
}


int ttusb_desc_device_str_prod( char * str )
{
    usbd_config_t * pcfg = &( uctx.ucfg );

    /**/
    pcfg->str_prod = str;
    return 0;
}


int ttusb_desc_device_str_seri( char * str )
{
    usbd_config_t * pcfg = &( uctx.ucfg );

    /**/
    pcfg->str_seri = str;
    return 0;
}


/**/
int ttusb_desc_config_str( char * str )
{
    usbd_config_t * pcfg = &( uctx.ucfg );

    /**/
    pcfg->str_cnfg = str;
    return 0;
}


int ttusb_desc_config_power( uint8_t pwr )
{
    usbd_config_t * pcfg = &( uctx.ucfg );

    /**/
    pcfg->maxpower = pwr;
    return 0;
}


/* config: interface, endpoint */
int ttusb_desc_interface_str( char * str )
{
    usbd_config_t * pcfg = &( uctx.ucfg );

    /**/
    pcfg->iface[0].str_intf = str;
    return 0;
}


/*
OUT : addr
 IN : 0x80 | addr
*/
static int ttusb_desc_interface_add_endp( uint8_t ios, bulk_proc_cbk cbkf )
{
    int  eidx;
    usbd_config_t * pcfg = &( uctx.ucfg );
    usbd_intf_t * pifc = &( pcfg->iface[0] );

    /**/
    if ( pcfg->used >= 15 )  {
        return 1;
    }

    if ( pifc->epcnt >= 4 )  {
        return 2;
    }

    /**/
    eidx = pcfg->used;
    pcfg->used += 1;
    pcfg->endps[eidx].pifc = pifc;
    pcfg->endps[eidx].pid = 0;
    pcfg->endps[eidx].pfunc = cbkf;

    /**/
    pifc->epadrs[pifc->epcnt] = (eidx + 1) | ios;
    pifc->epcnt += 1;
    return 0;
}


int ttusb_desc_interface_add_inp( bulk_proc_cbk cbkf )
{
    int iret;

    iret = ttusb_desc_interface_add_endp( USB_DIR_IN, cbkf );
    if ( 0 != iret )  {
        return iret;
    }

    return 0;
}


int ttusb_desc_interface_add_outp( bulk_proc_cbk cbkf )
{
    int iret;

    iret = ttusb_desc_interface_add_endp( USB_DIR_OUT, cbkf );
    if ( 0 != iret )  {
        return iret;
    }

    return 0;
}



/* 支持对长消息, 自动分片和重组;
 * H2D, D2H, 使用同一块缓存.
 */

#define CTRL_IDLE           0
#define CTRL_D2H_DATA       1
#define CTRL_D2H_STATUS     2
#define CTRL_H2D_DATA       3
#define CTRL_H2D_STATUS     4



/**/
static xfer_context ctx;

static bool should_set_address = false;
static uint8_t dev_addr = 0;


/* 一个空函数. */
static void  trans_end_nothing( void )
{
    printf( "ss, End.\n");
    printf( "\n");
}

static void  trans_end_set_address( void )
{
    usb_hw->dev_addr_ctrl = dev_addr;
    return;
}


static inline void endp_in_fill_trans( int idx, int pid, int tlen )
{
    int  i;
    uint32_t bufctrl;

    /* buffer ctrl */
    bufctrl = tlen & USB_BUF_CTRL_LEN_MASK;
    bufctrl = bufctrl | USB_BUF_CTRL_FULL | USB_BUF_CTRL_SEL;

    if ( pid == 0 )  {
        bufctrl = bufctrl | USB_BUF_CTRL_DATA0_PID;
    } else {
        bufctrl = bufctrl | USB_BUF_CTRL_DATA1_PID;
    }

    /**/
    usb_dpram->ep_buf_ctrl[idx].in = bufctrl;

    /* delay 12 cycle?? */
    for ( i=0; i<12; i++ )  {
        __asm volatile( "nop" );
    }

    /* update AVAILABLE bit */
    usb_dpram->ep_buf_ctrl[idx].in = bufctrl | USB_BUF_CTRL_AVAIL;
    return;
}


static inline void endp_out_prep_space( int idx, int pid )
{
    int  i;
    uint32_t bufctrl;

    /**/
    bufctrl = USB_BUF_CTRL_LEN_MASK;
    bufctrl = bufctrl | USB_BUF_CTRL_SEL;

    /**/
    if ( pid == 0 )  {
        bufctrl = bufctrl | USB_BUF_CTRL_DATA0_PID;
    } else {
        bufctrl = bufctrl | USB_BUF_CTRL_DATA1_PID;
    }

    /**/
    usb_dpram->ep_buf_ctrl[ idx ].out = bufctrl;

    /* delay 12 cycle?? */
    for ( i=0; i<12; i++ )  {
        __asm volatile( "nop" );
    }

    /* update AVAILABLE bit */
    usb_dpram->ep_buf_ctrl[ idx ].out = bufctrl | USB_BUF_CTRL_AVAIL;
    return;
}


/*
set IN, OUT : stall
*/
int  usbd_ctrl_stall( void )
{
	usb_hw_set->ep_stall_arm = USB_EP_STALL_ARM_EP0_IN_BITS;
    usb_dpram->ep_buf_ctrl[0].in = USB_BUF_CTRL_STALL;
    usb_dpram->ep_buf_ctrl[0].out = USB_BUF_CTRL_STALL;
    return 0;
}


uint32_t  _hw_endpoint_alloc( int tsiz )
{
    static uint32_t nextb = offsetof(usb_device_dpram_t, epx_data);
    uint32_t  tlast;

    /**/
    tlast = nextb;
    nextb += tsiz;
    return tlast;
}


/* 
setting : soft >> hardware
interface, end point >> register and dpram
*/
int usbd_setup_endpoints( void )
{
    int  i;
    int  ep;
    uint32_t  addr;
    usbd_intf_t * pifc = &( uctx.ucfg.iface[0] );

    /* todo : multi interface, multi bulk endp */
    for ( i=0; i<pifc->epcnt; i++ )  {

        if ( pifc->epadrs[i] & USB_DIR_IN )  {
            
            /* bulk in */
            ep = pifc->epadrs[i] & 0xF;
            addr = _hw_endpoint_alloc( 128 );

            /* bit[25:16] */
            usb_dpram->ep_ctrl[ep-1].in = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | (2 << EP_CTRL_BUFFER_TYPE_LSB) | addr;
            usb_dpram->ep_buf_ctrl[ep].in = 0;

        } else {

            /* bulk out */
            ep = pifc->epadrs[i] & 0xF;
            addr = _hw_endpoint_alloc( 128 );

            /* bit[25:16] */
            usb_dpram->ep_ctrl[ep-1].out = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | (2 << EP_CTRL_BUFFER_TYPE_LSB) | addr;
            usb_dpram->ep_buf_ctrl[ep].out = 0;

        }

    }

    /**/
    return 0;
}


int usbd_get_device_status( uint8_t * ptr )
{
    uint16_t * pu16;

    // Device status bit mask
    // - Bit 0: Self Powered
    // - Bit 1: Remote Wakeup enabled
    pu16 = (uint16_t *)ptr;
    pu16[0] = 0x0;
    pu16[1] = 0x0;

    /**/
    return sizeof(uint16_t);
}


// return: descriptor length.
int usbd_get_device_descriptor( usbd_config_t * pcfg, uint8_t * ptr )
{
    usb_device_descriptor_t * pdesc;

    /**/
    pdesc = (usb_device_descriptor_t *)ptr;
    pdesc->bLength = sizeof(usb_device_descriptor_t);
    pdesc->bDescriptorType = USB_DT_DEVICE;
    pdesc->bcdUSB = 0x0200;
    pdesc->bDeviceClass = 0;
    pdesc->bDeviceSubClass = 0;
    pdesc->bDeviceProtocol = 0;
    pdesc->bMaxPacketSize0 = 64;
    pdesc->idVendor = pcfg->vid;
    pdesc->idProduct = pcfg->pid;
    pdesc->bcdDevice = 0;
    pdesc->bNumConfigurations = 1;

    if ( pcfg->str_manu != NULL )  {
        pdesc->iManufacturer = 1;
    } else  {
        pdesc->iManufacturer = 0;
    }
    
    if ( pcfg->str_prod != NULL )  {
        pdesc->iProduct = 2;
    } else  {
        pdesc->iProduct = 0;
    }
    
    if ( pcfg->str_seri != NULL )  {
        pdesc->iSerialNumber = 3;
    } else  {
        pdesc->iSerialNumber = 0;
    }
    
    /**/
    return pdesc->bLength;
}


int usbd_get_config_descriptor( usbd_config_t * pcfg, int rlen, uint8_t * ptr )
{
    int  i;
    usbd_intf_t * pifc = &( pcfg->iface[0] );
    usb_configuration_descriptor_t * pdesc_cfg;
    usb_interface_descriptor_t * pdesc_intf;
    usb_endpoint_descriptor_t * pdesc_endps;

    /**/
    pdesc_cfg = (usb_configuration_descriptor_t *)ptr;
    pdesc_cfg->bLength = sizeof( usb_configuration_descriptor_t );
    pdesc_cfg->bDescriptorType = USB_DT_CONFIG;
    pdesc_cfg->wTotalLength = sizeof( usb_configuration_descriptor_t );
    pdesc_cfg->wTotalLength = pdesc_cfg->wTotalLength + sizeof( usb_interface_descriptor_t );
    pdesc_cfg->wTotalLength = pdesc_cfg->wTotalLength + ( pifc->epcnt * sizeof(usb_endpoint_descriptor_t) );
    pdesc_cfg->bNumInterfaces = 1;
    pdesc_cfg->bConfigurationValue = 1;
    
    if ( pcfg->str_cnfg != NULL )  {
        pdesc_cfg->iConfiguration = 4;
    } else  {
        pdesc_cfg->iConfiguration = 0;    
    }
    
    pdesc_cfg->bmAttributes = 0x80;
    pdesc_cfg->bMaxPower = pcfg->maxpower;

    /**/
    if ( rlen > sizeof( usb_configuration_descriptor_t ) )
    {
        /* interfaces */
        pdesc_intf = (usb_interface_descriptor_t *)(pdesc_cfg + 1);
        pdesc_intf->bLength = sizeof(usb_interface_descriptor_t);
        pdesc_intf->bDescriptorType = USB_DT_INTERFACE;
        pdesc_intf->bInterfaceNumber = 0;
        pdesc_intf->bAlternateSetting = 0;
        pdesc_intf->bNumEndpoints = (uint8_t)pifc->epcnt;
        pdesc_intf->bInterfaceClass = 0xFF;
        pdesc_intf->bInterfaceSubClass = 0;
        pdesc_intf->bInterfaceProtocol = 0;
        
        if ( pifc->str_intf != NULL )  {
            pdesc_intf->iInterface = 5;
        } else  {
            pdesc_intf->iInterface = 0;
        }

        /* end points */
        pdesc_endps = (usb_endpoint_descriptor_t *)(pdesc_intf + 1);
        for ( i=0; i<pifc->epcnt; i++ )  {
            pdesc_endps[i].bLength = sizeof(usb_endpoint_descriptor_t);
            pdesc_endps[i].bDescriptorType = USB_DT_ENDPOINT;
            pdesc_endps[i].bEndpointAddress = pifc->epadrs[i];
            pdesc_endps[i].bmAttributes = USB_TRANSFER_TYPE_BULK;
            pdesc_endps[i].wMaxPacketSize = 64;
            pdesc_endps[i].bInterval = 0;
        }
    }

    /**/
    return pdesc_cfg->wTotalLength;
}


int usbd_get_string_descriptor( usbd_config_t * pcfg, int idx, int lang, uint8_t * ptr )
{
    int  tofs;
    usbd_intf_t * pifc = &( pcfg->iface[0] );
    char * pstr;
    usb_string_descriptor_t * pdesc;

    /**/
    pdesc = (usb_string_descriptor_t *)ptr;
    pdesc->bDescriptorType = USB_DT_STRING;

    /**/
    switch (idx)  {
    case 0:
        /* Only support Language for english */
        pdesc->bLength = 4;
        pdesc->wArray[0] = 0x0409;
        return pdesc->bLength;    

    case 1:
        pstr = pcfg->str_manu;
        break;

    case 2:
        pstr = pcfg->str_prod;
        break;

    case 3:
        pstr = pcfg->str_seri;
        break;

    case 4:
        pstr = pcfg->str_cnfg;
        break;

    case 5:
        pstr = pifc->str_intf;
        break;
        
    case 0xEE:
        /* bVendorCode = 0x17 */
        pstr = "MSFT100\x17";
        break;

    default:
        pstr = NULL;
        break;
    }

    /* null string check */
    if ( pstr == NULL )  {
        return 0;
    }

    /* ascii to UNICODE(UTF-16LE) */
    tofs = 0;
    while ( *pstr != '\0' )  {
        pdesc->wArray[tofs] = *pstr;
        tofs += 1;
        pstr += 1;
    }

    pdesc->bLength = 2 + (uint8_t)(tofs << 1);

    /**/
    return pdesc->bLength;
}




int usbd_intf_start( usbd_context_t * pctx, usbd_intf_t * pifc )
{
    int  i;
    int  idx;
    usbd_endp_t * pedp;

    /**/
    for ( i=0; i<pifc->epcnt; i++ )  {

        if ( (pifc->epadrs[i] & USB_DIR_IN) == 0 )  {
            
            idx = pifc->epadrs[i];
            pedp = &( pctx->ucfg.endps[idx - 1] );
            printf("start, %d\n", idx );

            /**/
            endp_out_prep_space( idx, pedp->pid );
            pedp->pid = pedp->pid ^ 0x1;
        }
    }

    /**/
    return 0;
}


/*
host 可能附带一块数据, 参数 pkt->wLength 就是这段数据的长度.
这块数据的内容就在 ptr 指向的内存中.
*/
int usbd_setup_h2d_proc( usbd_context_t * pctx, usb_setup_packet_t * pkt, uint8_t * ptr, trans_end_cbk * pcbk )
{
    usbd_intf_t * pifc = &( pctx->ucfg.iface[0] );
    uint8_t rtype = pkt->bmRequestType;
    uint8_t req = pkt->bRequest;

    /**/    
    if ( (rtype & USB_REQ_TYPE_TYPE_MASK) != USB_REQ_TYPE_TYPE_STANDARD )  {
        /* todo: (CLASS, VENDOR) */
        printf( "rtype, todo-0: %x\n", rtype );
        return 0;
    }

    if ( (rtype & USB_REQ_TYPE_RECIPIENT_MASK) != USB_REQ_TYPE_RECIPIENT_DEVICE )  {
        /* todo: ( INTERFACE, ENDPOINT ) */
        printf( "rtype, todo-1: %x\n", rtype );
        return 0;
    }

    /* STANDARD, DEVICE */
    switch (req)  {
    case USB_REQUEST_SET_ADDRESS:
        dev_addr = (pkt->wValue & 0xff);
        *pcbk = trans_end_set_address;
        break;

    case USB_REQUEST_SET_CONFIGURATION:
        /* todo : xxxx */
        /* interface start work.. */
        usbd_intf_start( pctx, pifc );
        break;

    default:
        break;
    }

    return 0;
}


int usbd_setup_d2h_vendor( usb_setup_packet_t * pkt, uint8_t * ptr, trans_end_cbk * pcbk )
{
    int tsiz;
    uint8_t rtype = pkt->bmRequestType;
    uint8_t req = pkt->bRequest;
    usb_msos_compatid_descriptor_t * pcmp_desc;
    usb_msos_function_section_t * pfun_sec;
    usb_msos_ext_properties_descriptor_t * pext_desc;
    usb_msos_custom_property_section_t * pcus_sec;

    /**/
    if ( (rtype & USB_REQ_TYPE_RECIPIENT_MASK) != USB_REQ_TYPE_RECIPIENT_DEVICE )  {
        return 0;
    }

    /* MSFT100\x17"; */
    if ( req != 0x17 )  {
        return 0;
    }

    /**/
    switch ( pkt->wIndex )  {

    case 0x4:

        /**/
        tsiz = sizeof(usb_msos_compatid_descriptor_t) + sizeof(usb_msos_function_section_t);
        memset( ptr, 0, tsiz );

        /**/
        pcmp_desc = (usb_msos_compatid_descriptor_t *)ptr;
        pcmp_desc->dwLength = tsiz;
        pcmp_desc->bcdVersion = 0x0100;
        pcmp_desc->wIndex = 4;
        pcmp_desc->bCount = 1;
        
        /**/
        pfun_sec = (usb_msos_function_section_t *)(pcmp_desc + 1);
        memset( pfun_sec, 0, sizeof(usb_msos_function_section_t) );
        pfun_sec->bInterfaceNumber = 0;
        strcpy( (char *)pfun_sec->compatibleID, "WINUSB" );

        /**/
        break;

    case 0x5:

        /* todo */
        pext_desc = (usb_msos_ext_properties_descriptor_t *)ptr;
        pext_desc->dwLength = sizeof(usb_msos_ext_properties_descriptor_t);
        pext_desc->bcdVersion = 0x0100;
        pext_desc->wIndex = 5;
        pext_desc->wCount = 1;

        /**/
        pcus_sec = (usb_msos_custom_property_section_t *)(pext_desc + 1);
        pcus_sec->dwPropertyDataType = 1;
        pcus_sec->dwPropertyNameLength = 32;
        pcus_sec->dwSize = 65;

        /**/
        pext_desc->dwLength = pext_desc->dwLength + pcus_sec->dwSize;
        tsiz = pext_desc->dwLength;
        break;

    default:
        tsiz = 0;
        break;
    }

    /**/
    return tsiz;
}


/*
 可以返回数据给 host:
    这个函数的 return 值表示返回数据的字节长度.
    返回的数据内容就放在参数 ptr 指向的内存块中.
*/
int usbd_setup_d2h_proc( usbd_config_t * pcfg, usb_setup_packet_t * pkt, uint8_t * ptr, trans_end_cbk * pcbk )
{
    int  iret = 0;
    uint8_t rtype = pkt->bmRequestType;
    uint8_t req = pkt->bRequest;
    uint16_t dtype;

    if ( (rtype & USB_REQ_TYPE_TYPE_MASK) != USB_REQ_TYPE_TYPE_STANDARD )  {
        return usbd_setup_d2h_vendor( pkt, ptr, pcbk );
    }

    if ( (rtype & USB_REQ_TYPE_TYPE_MASK) != USB_REQ_TYPE_TYPE_STANDARD )  {
        /* todo: (CLASS, VENDOR) */
        printf( "rtype, todo-0: %x\n", rtype );
        return 0;
    }

    if ( (rtype & USB_REQ_TYPE_RECIPIENT_MASK) != USB_REQ_TYPE_RECIPIENT_DEVICE )  {
        /* todo: ( INTERFACE, ENDPOINT ) */
        printf( "rtype, todo-1: %x\n", rtype );
        return 0;
    }

    switch ( req )  {
    case USB_REQUEST_GET_STATUS:
        iret = usbd_get_device_status( ptr );
        break;

    case USB_REQUEST_GET_DESCRIPTOR:
        dtype = pkt->wValue >> 8;

        switch ( dtype )  {
        case USB_DT_DEVICE:
            iret = usbd_get_device_descriptor( pcfg, ptr );
            break;

        case USB_DT_CONFIG:
            iret = usbd_get_config_descriptor( pcfg, pkt->wLength, ptr );
            break;

        case USB_DT_STRING:
            iret = usbd_get_string_descriptor( pcfg, pkt->wValue & 0xFF, pkt->wIndex, ptr );
            break;
        
        case USB_DT_BOS:
            /* todo : bos ... */
            break;

        case USB_DT_DEVICE_QUALIFIER:
            break;
            
        default:
            break;
        }

        break;

    case USB_REQUEST_GET_CONFIGURATION:
        break;

    default:
        printf( "todo:get, %u\n", req );
        break;
    }

    /**/
    return iret;
}



void usb_ep0_setup_handler( usbd_context_t * pctx, usb_setup_packet_t * pkt )
{
    int  iret = 0;
    int  tlen;
    xfer_context * pxfer = &( pctx->uxfer );
    uint16_t  tmax = pkt->wLength;
    uint8_t rtype = pkt->bmRequestType;

    /**/
    pxfer->pkt = pkt;

    /* debug */
    if ( pxfer->stg != CTRL_IDLE )  {
        printf( "setup, not IDLE\n\n" );
    }

    
    if ( (rtype & 0x80) != 0 )  {
        
        /* Device To Host */
        pxfer->cbkf = trans_end_nothing;
        iret = usbd_setup_d2h_proc( &(pctx->ucfg), pkt, pxfer->buf, &pxfer->cbkf );
        

        if ( iret <= 0 )  {
        	
            usbd_ctrl_stall();
            
        } else if ( tmax > 0 )  {

            /* todo : tmax 是 host 期望返回的字节数:
             * 1) 实际上函数可能返回更多, 比如第一次 get device descriptor, host 仅仅期望 8 个字节.
             * 2) 还有一种是 device 不支持这个 request, 函数直接返回 0, 这个情况???
             */
            pxfer->stg = CTRL_D2H_DATA;
            pxfer->pid = 1;
            pxfer->ofs = 0;

            if ( iret > tmax )  {
                pxfer->max = tmax;
            } else  {
                pxfer->max = iret;
            }

            /**/
            tlen = pxfer->max - pxfer->ofs;
            if ( tlen > 64 )  {
                iret = 64;
            }

            /**/
            memcpy( &usb_dpram->ep0_buf_a[0], pxfer->buf, tlen );
            endp_in_fill_trans( 0, pxfer->pid, tlen );

            pxfer->pid ^= 0x1;
            pxfer->ofs += tlen;

        } else {

            /* No DATA?? panic?? */
            printf("D2H, wLength=0??\n");
        }

        printf( "D2H, %d, %x, %x, %u, %d\n", pkt->bRequest, pkt->wValue, pkt->wIndex, pkt->wLength, iret );

    } else {

        /* Host To Device */
        if ( tmax > 0 )  {

            pxfer->stg = CTRL_H2D_DATA;
            pxfer->max = tmax;
            pxfer->ofs = 0;
            pxfer->pid = 1;

            /**/
            endp_out_prep_space( 0, pxfer->pid );
            pxfer->pid ^= 0x1;

        } else {

            /* No Data */
            pxfer->cbkf = trans_end_nothing;
            usbd_setup_h2d_proc( pctx, pkt, pxfer->buf, &pxfer->cbkf );

            /**/
            pxfer->stg = CTRL_H2D_STATUS;
            endp_in_fill_trans( 0, 1, 0 );
        }
    
        /**/
        printf( "H2D, %d, %x, %u\n", pkt->bRequest, pkt->wValue, pkt->wLength );
    }

    return;
}


void usbd_ep0_in_handler( usbd_context_t * pctx )
{
    int  tlen;
    xfer_context * pxfer = &( pctx->uxfer );

    if ( pxfer->stg == CTRL_D2H_DATA )  {

        /* split, next ... */
        if ( pxfer->max == 0 )  {

            /* change stage to status */
            pxfer->stg = CTRL_D2H_STATUS;
            endp_out_prep_space( 0, 1 );

        } else if ( pxfer->ofs < pxfer->max )  {

            tlen = pxfer->max - pxfer->ofs;
            if ( tlen > 64 )  {
                tlen = 64;
            }

            /**/
            memcpy( &usb_dpram->ep0_buf_a[0], &pxfer->buf[pxfer->ofs], tlen );
            endp_in_fill_trans( 0, pxfer->pid, tlen );

            /**/
            pxfer->pid ^= 0x1;
            pxfer->ofs += tlen;

        } else if ( (pxfer->ofs == pxfer->max) && ((pxfer->max % 64) == 0) )  {

            /* align to 64, the last slice is zero. */
            endp_in_fill_trans( 0, pxfer->pid, 0 );

            /* add 1 to ofs */
            pxfer->pid ^= 0x1;
            pxfer->ofs += 1;

        } else  {

            /* change stage to status */
            pxfer->stg = CTRL_D2H_STATUS;
            endp_out_prep_space( 0, 1 );
        }

    } else if ( pxfer->stg == CTRL_H2D_STATUS )  {

        /* cbk function */
        pxfer->cbkf();

        /**/
        pxfer->stg = CTRL_IDLE;

    } else  {

        /* error */
        // usb_hw_set->ep_stall_arm = ??;
        printf( "ep0, in, end, %d", pxfer->stg );
    }

}


void usbd_ep0_out_handler( usbd_context_t * pctx )
{
    xfer_context * pxfer = &( pctx->uxfer );
    uint32_t  temp;
    int  tlen;

    /**/
    if ( pxfer->stg == CTRL_H2D_DATA )  {

        /* merge to packet */
        temp = usb_dpram->ep_buf_ctrl[0].out;
        assert( temp & USB_BUF_CTRL_FULL );

        /**/
        tlen = temp & USB_BUF_CTRL_LEN_MASK;        
        memcpy( &pxfer->buf[pxfer->ofs], &usb_dpram->ep0_buf_a[0], tlen );
        pxfer->ofs += tlen;

        if ( pxfer->ofs < pxfer->max )  {
            /**/
            endp_out_prep_space( 0, pxfer->pid );
            pxfer->pid ^= 0x1;

        } else {

            /* call proc function.. */
            pxfer->cbkf = trans_end_nothing;
            usbd_setup_h2d_proc( pctx, pxfer->pkt, pxfer->buf, &pxfer->cbkf );

            /**/
            pxfer->stg = CTRL_H2D_STATUS;
            endp_in_fill_trans( 0, 1, 0 );
        }

    } else if ( pxfer->stg == CTRL_D2H_STATUS )  {

        /* cbk function */
        pxfer->cbkf();

        /**/
        pxfer->stg = CTRL_IDLE;

    } else {

        /* error */
        // usb_hw_set->ep_stall_arm = ??;
        printf( "ep0, out, end, %d", pxfer->stg );
    }
}

/*
*/
void usbd_epxx_in_handler( usbd_context_t * pctx, int idx )
{
    int iret;
    uint8_t info;
    int tofs;
    usbd_endp_t * pedp;
    usbd_intf_t * pifc;

    /**/
    pedp = &( pctx->ucfg.endps[idx - 1] );
    pifc = pedp->pifc;
    
    /* 15:6 */
    tofs = usb_dpram->ep_ctrl[idx - 1].in & 0xFFC0;
    tofs = tofs - 0x180;
    iret = pedp->pfunc( pifc, &( usb_dpram->epx_data[tofs] ), 64 );

    if ( iret > 0 )  {

        endp_in_fill_trans( idx, pedp->pid, iret );
        pedp->pid = pedp->pid ^ 0x1;

    } else {

        /* noting to trans to host: STALL or NACK?? */
    }

    /**/
    return;
}


void usbd_epxx_out_handler( usbd_context_t * pctx, int idx )
{
    int iret;
    int tofs;
    int tlen;
    usbd_endp_t * pedp;
    usbd_intf_t * pifc;


    /**/
    pedp = &( pctx->ucfg.endps[idx - 1] );
    pifc = pedp->pifc;
    
    /* 15:6 */
    tofs = usb_dpram->ep_ctrl[ idx - 1 ].out & 0xFFC0;
    tofs = tofs - 0x180;
    tlen = usb_dpram->ep_buf_ctrl[ idx  ].out & USB_BUF_CTRL_LEN_MASK;
    iret = pedp->pfunc( pifc, &( usb_dpram->epx_data[tofs] ), tlen );
    
    if ( iret == 0 )  {
        int  i;
        uint32_t  bufctrl;

        /* continue recv from host */
        endp_out_prep_space( idx, pedp->pid );
        pedp->pid = pedp->pid ^ 0x1;
    }

    return;
}


void usbd_bus_reset( void )
{
    xfer_context * pctx;

    /**/
    pctx = &ctx;
    pctx->stg = CTRL_IDLE;
    pctx->pkt = NULL;
    pctx->max = 0;
    pctx->ofs = 0;

    /**/
    usb_hw->dev_addr_ctrl = 0;

    /**/
    printf( "bus reset..\n" );
    printf( "\n" );
    return;
}


static void USBctrl_handle_buff_status( usbd_context_t * pctx )
{
    int idx;
    uint32_t buffers = usb_hw->buf_status;

    /* write to clear */
    usb_hw_clear->buf_status = buffers;

    
    if ( buffers & 0x1 )  {
        /* EP0, IN.. */
        usbd_ep0_in_handler( pctx );
    }

    if ( buffers & 0x2 )  {
        /* EP0, OUT.. */
        usbd_ep0_out_handler( pctx );
    }

    /* __builtin_ffs  */
    buffers = buffers & 0xFFFC;
    while ( buffers != 0 )  {
        
        idx = __builtin_ctz( buffers );
        
        // printf( "bufs %x, %d\n", buffers, idx );
        
        buffers = buffers ^ ( 0x1 << idx );

        /* idx >> { interface, in_out, endp_idx } */
        if ( idx & 0x1 )  {

            /* 3, 5, 7, ... : OUT endp */
            idx = idx >> 1;
            usbd_epxx_out_handler( pctx, idx );

        } else {

            /* 2, 4, 6, ... : IN endp */
            idx = idx >> 1;
            usbd_epxx_in_handler( pctx, idx );
        }
        
    }

    return;
}

// USB interrupt handler
void USBctrl_Handler( void )
{
    usbd_context_t * pctx = &uctx;
    uint32_t status = usb_hw->ints;
    uint32_t handled = 0;

    // Buffer status, one or more buffers have completed
    if (status & USB_INTS_BUFF_STATUS_BITS) {
        handled |= USB_INTS_BUFF_STATUS_BITS;
        USBctrl_handle_buff_status( pctx );
    }

    // Setup packet received
    if (status & USB_INTS_SETUP_REQ_BITS) {
        handled |= USB_INTS_SETUP_REQ_BITS;
        usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
        usb_ep0_setup_handler( pctx, (usb_setup_packet_t *)&usb_dpram->setup_packet );
    }

    // Bus is reset
    if (status & USB_INTS_BUS_RESET_BITS) {
        handled |= USB_INTS_BUS_RESET_BITS;
        usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
        usbd_bus_reset();
    }

    if (status ^ handled) {
        printf("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
    }

}


int  ttusb_init( void )
{
    // Reset usb controller
    reset_block(RESETS_RESET_USBCTRL_BITS);
    unreset_block_wait(RESETS_RESET_USBCTRL_BITS);

    // Clear any previous state in dpram just in case
    memset(usb_dpram, 0, sizeof(*usb_dpram)); // <1>

    // Enable USB interrupt at processor
    NVIC_EnableIRQ( USBCTRL_IRQ_IRQn );

    // Mux the controller to the onboard usb phy
    usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;

    // Force VBUS detect so the device thinks it is plugged into a host
    usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;

    // Enable the USB controller in device mode.
    usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;

    // Enable an interrupt per EP0 transaction
    usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS; // <2>

    // Enable interrupts for when a buffer is done, when the bus is reset,
    // and when a setup packet is received
    usb_hw->inte = USB_INTS_BUFF_STATUS_BITS |
                   USB_INTS_BUS_RESET_BITS |
                   USB_INTS_SETUP_REQ_BITS;

    // Set up endpoints (endpoint control registers)
    // described by device configuration
    usbd_setup_endpoints();

    // Present full speed device by enabling pull up on DP
    usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;

    return 0;
}


int ttusb_intf_send( usbd_intf_t * pifc, int idx, int tlen, void * pdat )
{
    usbd_context_t * pctx = &uctx;
    usbd_endp_t * pedp;
    int tofs;

    /* todo */
    pifc = &( uctx.ucfg.iface[0] );

    /**/
    if ( idx >= pifc->epcnt ) {
        return 1;
    }

    /* check or assert? */
    idx = pifc->epadrs[idx];
    if ( (idx & 0x80) == 0 ) {
        return 3;
    }

    idx = idx & 0x7F;

    /* buffer not availid */
    if ( 0 != (usb_dpram->ep_buf_ctrl[idx].in & USB_BUF_CTRL_AVAIL) ) {
        return 4;
    }

    //
    pedp = &( pctx->ucfg.endps[idx - 1] );
    
    /* todo : check endp status, full? */
    // printf( "ep: %d, pid : %d\n", idx, pedp->pid );

    /* 15:6 */
    tofs = usb_dpram->ep_ctrl[ idx - 1 ].in & 0xFFC0;
    tofs = tofs - 0x180;
    memcpy( &( usb_dpram->epx_data[tofs] ), pdat, tlen );
    endp_in_fill_trans( idx, pedp->pid, tlen );

    /* toggle pid */
    pedp->pid = pedp->pid ^ 0x1;
    return 0;
}

